31. Procesamiento de XML¶
XML es un lenguaje de marcado para escribir documentos estructurados. Se usa para multitud de aplicaciones y por tanto deben conocerse al menos los rudimentos.
Un documento XML está jerárquicamente dividido por elementos
identificados por etiquetas (tags). La etiqueta que marca el comienzo
se identifica con una palabra entre corchetes angulares, mientras que la
etiqueta que marca el final se identifica con un carácter /
adicional antes de la misma palabra. Por ejemplo:
<root> ... </root>
Dentro de un elemento puede haber otros elementos. Por ejemplo:
<root> <origen>...</origen> <nombre>...</nombre> </root>
Cada etiqueta puede tener atributos que se incluyen justo después de la etiqueta de comienzo con los valores entre comillas:
<root id="28013" version="1.0"> ... </root>
Un elemento también puede contener texto entre las etiquetas de comienzo y fin:
<nombre>Aranjuez</nombre>
Un documento XML bien formado solo contiene un elemento raíz (no contenido en ningún otro elemento). Por tanto al leer un documento XML se genera un arbol de elementos.
La biblioteca estándar de Python incluye varios módulos relacionados con la lectura y escritura de documentos XML. Nosotros solo comentaremos el más sencillo. Consulta la documentación de Python si necesitas ampliar más.
31.1. Leer con ElementTree
¶
El procesador de XML más simple y ligero de los incluidos en Python es
xml.etree.ElementTree
. Ilustraremos su uso con un archivo XML como
los empleados en el trabajo en grupo.
import requests
r = requests.get('http://www.aemet.es/xml/municipios/localidad_28013.xml')
xml = r.text.encode('utf-8')
Ahora la variable xml
contiene una cadena con todo el documento XML.
Veamos cómo interpretarlo con ElementTree
.
import xml.etree.ElementTree as ET
root = ET.fromstring(xml)
La función fromstring
devuelve un objeto de tipo Element
, el
elemento raíz del documento XML. Element
permite acceder a todos los
componentes de un elemento con estas funciones:
e.iter('etiqueta')
devuelve todos los elementosetiqueta
que haya dentro del elementoe
. Incluso los que estén dentro de otros elementos contenidos dentro dee
.e.find('etiqueta')
devuelve el primer elementoetiqueta
que es hijo directo dee
.e.findall('etiqueta')
devuelve todos los elementosetiqueta
que son hijos directos dee
.e.text
devuelve la cadena correspondiente al texto del elementoe
.e.attrib
devuelve los atributos del elementoe
como un diccionario.e.get('attr')
devuelve el valor del atributoattr
del elementoe
.
Veamos un ejemplo que imprime la humedad mínima y máxima para los próximos días.
for dia in root.iter('dia'):
print '{0}:'.format(dia.get('fecha')),
humedad = dia.find('humedad_relativa')
hmax = int(humedad.find('maxima').text)
hmin = int(humedad.find('minima').text)
print 'humedad {0}/{1}'.format(hmin, hmax)
2015-12-25: humedad 70/100
2015-12-26: humedad 55/100
2015-12-27: humedad 45/100
2015-12-28: humedad 60/95
2015-12-29: humedad 75/100
2015-12-30: humedad 65/95
2015-12-31: humedad 90/100
31.2. Leer con BeautifulSoup
¶
Nota: En *BeautifulSoup* se usan atributos de Python para representar elementos XML. No confundas los *atributos* de Python con los *atributos* de un elemento XML. Los atributos de XML son parejas clave/valor que se pueden incluir en las etiquetas de comienzo de cada elemento.
BeautifulSoup es una biblioteca que simplifica notablemente la lectura
y escritura de documentos XML. En BeautifulSoup la jerarquía del
documento se traslada automáticamente a Python en forma de atributos de
objeto. Así, por ejemplo, si el documento está contenido en un elemento
root
entonces lo devuelto por BeautifulSoup tendrá un atributo
root
.
from bs4 import BeautifulSoup
soup = BeautifulSoup(xml, 'lxml')
Así, soup
tendrá un atributo root
y a su vez ese atributo tendrá
un atributo prediccion
, etc. Tenemos varias formas de recorrer los
elementos. Si usamos los paréntesis como si soup
fuera una función
podemos buscar todos los elementos con una etiqueta determinada. Por
ejemplo, soup('dia')
nos devolverá todos los elementos con etiqueta
dia
. En cambio si usamos los corchetes, como si se tratara de una
lista, podemos acceder a los atributos del elemento. Por ejemplo si
dia
es un elemento con etiqueta 'dia'
entonces dia['fecha']
es el valor del atributo fecha
del elemento dia
en el documento
XML.
Si solo hay un elemento con esa etiqueta entonces podemos usar el
atributo con el mismo nombre. Por ejemplo, los elementos con etiqueta
dia
tienen solo un elemento humedad_relativa
. Por tanto podemos
acceder a él usando el atributo del mismo nombre. Si hay múltiples
elementos con la misma etiqueta el atributo solo sirve para acceder al
primero.
Para obtener el texto de cada elemento podemos acceder al atributo
string
. Por ejemplo,
soup.root.dia.prediccion.humedad_relativa.maxima.string
es el texto
del elemento maxima
, dentro del elemento humedad_relativa
,
dentro del elemento prediccion
, dentro del primer elemento dia
,
dentro del elemento root
del documento.
Así, el código equivalente al ejemplo de ElementTree
sería:
for dia in soup('dia'):
humedad = dia.humedad_relativa
hmax = int(humedad.maxima.string)
hmin = int(humedad.minima.string)
print '{0}: humedad {1}/{2}'.format(dia['fecha'], hmin, hmax)
2015-12-25: humedad 70/100
2015-12-26: humedad 55/100
2015-12-27: humedad 45/100
2015-12-28: humedad 60/95
2015-12-29: humedad 75/100
2015-12-30: humedad 65/95
2015-12-31: humedad 90/100